/*
 * $Id: MagnetMonitorEventProcessor.java,v 1.6 2009/08/24 20:18:27 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.processor.magmon;

import java.io.PrintWriter;
import java.util.Date;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.exceptions.BasicException;
import com.ge.healthcare.autosc.common.exceptions.PpadsCommandUtilException;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.PpadsCommandUtil;
import com.ge.healthcare.autosc.common.util.dataItem.IASCTextDataItem;
import com.ge.healthcare.autosc.common.util.dataItem.IASCTextReader;
import com.ge.healthcare.autosc.processor.PrcCodeConstants;
import com.ge.healthcare.autosc.processor.exception.MagnetMonitorProcessorException;
import com.ge.healthcare.autosc.processor.exception.MagnetMonitorSiteException;
import com.ge.healthcare.autosc.processor.shemjob.ShemLogger;
import com.ge.healthcare.autosc.processor.shemjob.guidance.dataset.MagnetDataChannelDef;
import com.ge.healthcare.autosc.processor.shemjob.guidance.dataset.MagnetDataChannelDefList;
import com.ge.healthcare.autosc.processor.shemjob.guidance.dataset.MinorEvent;
import com.ge.healthcare.autosc.processor.shemjob.guidance.loader.MagmonGuidanceLoader;

public class MagnetMonitorEventProcessor {

	/**
	 * Magnet Monitor Site object
	 */
	private IMagnetMonitorSite magMonSite = null;

	/**
	 * System Id for the request
	 */
	private String systemId = null;
	/**
	 * Major function of the request
	 */
	private String majorFunction = null;
	/**
	 * Minor function of the request
	 */
	private String minorFunction = null;

	private MagmonGuidanceLoader loader = null;

	/**
	 * Utility class with magnet monitor methods.
	 */
	private MagnetMonitorUtility utility = new MagnetMonitorUtility();

	public MagnetMonitorEventProcessor(String systemId, String majorFunction,
			String minorFunction, MagmonGuidanceLoader loader,
			IMagnetMonitorSite magMonSite)
			throws MagnetMonitorProcessorException {
		if (magMonSite == null || systemId == null) {
			throw new MagnetMonitorProcessorException(
					"Programming error. Incorrect use of class "
							+ MagnetMonitorDataProcessor.class.getName(),
					PrcCodeConstants.PRC_F_SHM_015);
		}
		this.magMonSite = magMonSite;
		this.systemId = systemId;
		this.loader = loader;
		this.majorFunction = majorFunction;
		this.minorFunction = minorFunction;
	}

	public void process(MinorEvent event)
			throws MagnetMonitorProcessorException {
		if (event != null) {
			final int functionId = event.getEventFunction();
			String eventText = event.getEventText();
			switch (functionId) {
			case MinorEvent.FILL_NOTIF_FUNC:
				doFillNotifFunc(eventText);
				break;
			case MinorEvent.TOUCH_FUNC:
				doTouchFunc(eventText);
				break;
			case MinorEvent.LIMIT_ALARM_FUNC:
				doLimitAlarmFunc(eventText);
				break;
			case MinorEvent.DISCRETE_ALARM_FUNC:
				doDiscreteAlarmFunc(eventText);
				break;
			case MinorEvent.UNKNOWN_ALARM_FUNC:
				doUnknownAlarmFunc(eventText);
				break;
			case MinorEvent.CHECKIN_FUNC:
				doCheckinFunc(eventText);
				break;
			case MinorEvent.UNKNOWN_MAJOR_FUNC:
				doUnknownMajorFunc();
				break;
			default:
				String message = new StringBuffer().append(
						"No processing defined for function: ").append(
						"  request type: ").append(majorFunction).append(
						"  task identifier: ").append(minorFunction).append(
						"  guidance function identifier: ").append(functionId)
						.toString();
				throw new MagnetMonitorProcessorException(message,
						PrcCodeConstants.PRC_F_SHM_013);
			}
		}
	}

	/**
	 * Do the fill notification function.
	 * 
	 * @param eventText
	 *            Text describing the event for the calls log.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected processing the event.
	 */
	private void doFillNotifFunc(String eventText)
			throws MagnetMonitorProcessorException {
		try {
			postToCallLog(eventText);
			PpadsCommandUtil.postPpadsEvent(systemId, PpadsCommandUtil.FILL,
					minorFunction);
		} catch (PpadsCommandUtilException pcue) {
			throw new MagnetMonitorProcessorException(pcue);
		}
	}

	/**
	 * Do the touch function.
	 * 
	 * @param eventText
	 *            Text describing the event for the calls log.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected processing the event.
	 */
	private void doTouchFunc(String eventText)
			throws MagnetMonitorProcessorException {
		try {
			postToCallLog(eventText);
			PpadsCommandUtil.postPpadsEvent(systemId, PpadsCommandUtil.TOUCH,
					minorFunction);
		} catch (PpadsCommandUtilException pcue) {
			throw new MagnetMonitorProcessorException(pcue);
		}
	}

	/**
	 * Do the limit alarm function.
	 * 
	 * @param eventText
	 *            Text describing the event for the calls log.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected processing the event.
	 */
	private void doLimitAlarmFunc(String eventText)
			throws MagnetMonitorProcessorException {
		try {
			postToCallLog(eventText);
			PpadsCommandUtil.postPpadsEvent(systemId,
					PpadsCommandUtil.LIMIT_ALARM, minorFunction);
		} catch (PpadsCommandUtilException pcue) {
			throw new MagnetMonitorProcessorException(pcue);
		}
	}

	/**
	 * Do the discrete alarm function.
	 * 
	 * @param eventText
	 *            Text describing the event for the calls log.
	 * 
	 * @param eventMarker
	 *            Marker defining the event for event-specific processing.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected processing the event.
	 */
	private void doDiscreteAlarmFunc(String eventText)
			throws MagnetMonitorProcessorException {
		try {
			postToCallLog(eventText);
			PpadsCommandUtil.postPpadsEvent(systemId,
					PpadsCommandUtil.DISCRETE_ALARM, minorFunction);
		} catch (PpadsCommandUtilException pcue) {
			throw new MagnetMonitorProcessorException(pcue);
		}
	}

	/**
	 * Do the unknown alarm function.
	 * 
	 * @param eventText
	 *            Text describing the event for the calls log.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected processing the event.
	 */
	private void doUnknownAlarmFunc(String eventText)
			throws MagnetMonitorProcessorException {
		try {
			ASCLogger.error("Processing unknown discrete alarm: "
					+ minorFunction);
			postToCallLog(eventText);
			PpadsCommandUtil.postPpadsEvent(systemId,
					PpadsCommandUtil.DISCRETE_ALARM, minorFunction);
		} catch (PpadsCommandUtilException pcue) {
			throw new MagnetMonitorProcessorException(pcue);
		}
	}

	/**
	 * Do the check-in function. This extracts the IRI data from the last line
	 * of the magnet data and both sends it to the database and uses it as the
	 * IRI data for posting the PPADS event.
	 * 
	 * @param eventText
	 *            Text describing the event for the calls log.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected processing the event.
	 */
	private void doCheckinFunc(String eventText)
			throws MagnetMonitorProcessorException {
		try {
			postToCallLog(eventText);
			MagmonIriData iriData = getIriData();
			iriData.writeToDatabaseWithTransaction();
			String nameValuePairs = iriData.getNameValuePairs();
			PpadsCommandUtil.postPpadsEvent(systemId, PpadsCommandUtil.IRI,
					minorFunction, nameValuePairs);
		} catch (MagnetMonitorSiteException mmse) {
			throw new MagnetMonitorProcessorException(mmse);
		} catch (PpadsCommandUtilException pcue) {
			throw new MagnetMonitorProcessorException(pcue);
		} catch (BasicException be) {
			throw new MagnetMonitorProcessorException(be);
		}
	}

	/**
	 * Process the case where there is no processing defined for the major
	 * function.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected processing the event.
	 */
	private void doUnknownMajorFunc() throws MagnetMonitorProcessorException {
		String message = "Unknown request type: " + majorFunction;
		throw new MagnetMonitorProcessorException(message);
	}

	/**
	 * Post the specified data to the site's call log. The line appended to the
	 * log includes a timestamp and the specified message.
	 * 
	 * @param messageText
	 *            The message text to append to the call log.
	 * 
	 * @exception AscServiceAbortException
	 *                Thrown if the processing should be aborted.
	 */
	private void postToCallLog(String messageText)
			throws MagnetMonitorProcessorException {
		StringBuffer buffer = new StringBuffer(160);
		Date currentTime = new Date();
		final int integerTime = (int) (currentTime.getTime() / 1000L);
		utility.appendBrokenOutTimeTo(buffer, integerTime, '/', ' ', ':', true);
		buffer.append(' ');
		buffer.append(messageText);
		try {
			IASCTextDataItem callLogItem = magMonSite.get("calls.log");
			appendLineTo(buffer.toString(), callLogItem);
		} catch (BasicException be) {
			throw new MagnetMonitorProcessorException(be);
		}
	}

	/**
	 * Append the specified line of text to the specified data item.
	 * 
	 * @param textLine
	 *            The line of text to be appended to the data item.
	 * 
	 * @param appendTarget
	 *            The data item to which to append the text.
	 * 
	 * @exception AscServiceAbortException
	 *                Thrown if the processing should be aborted.
	 */
	private void appendLineTo(String textLine, IASCTextDataItem appendTarget)
			throws BasicException {
		PrintWriter outWriter = appendTarget.openForWriting(true);
		try{
			outWriter.println(textLine);
			if (outWriter.checkError()) {
				String message = "Unable to append to " + appendTarget;
				throw new BasicException(message);
			}
		}finally{
			outWriter.close();
		}
	}

	/**
	 * Get the IRI data from the last line of the magnet data.
	 * 
	 * @return MagmonIriData The IRI data from the last line of the magnet data.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected reading the SHeM type, reading the
	 *                magnet data, or getting the IRI data from the last line of
	 *                the magnet data.
	 */
	private MagmonIriData getIriData() throws MagnetMonitorProcessorException,
			MagnetMonitorSiteException {
		MagnetDataChannelDef[] channelDefList = getChannelDefList();
		MagmonIriData iriData = null;

		IASCTextDataItem magDataItem = magMonSite.get("magdata.dat");
		try {
			IASCTextReader myReader = magDataItem.openForReading();
			String lastLine = readForLastLine(myReader);

			if (lastLine == null) {
				String emptyMessage = "Error detected while processing "
						+ "magnet data: magdata.dat is empty";
				throw new BasicException(emptyMessage);
			}
			iriData = new MagmonIriData(lastLine, systemId, channelDefList);
			IMagmonIriData iriDataFromContext = (IMagmonIriData) ApplicationInfo
					.getApplicationContext().getBean("magmonIriData");
			iriData.setMagmonIriData(iriDataFromContext);
		} catch (MagnetMonitorProcessorException mmpe) {
			throw mmpe;
		} catch (BasicException be) {
			throw new MagnetMonitorProcessorException(
					"Error was detected while processing magdata.dat", be);
		}
		return iriData;
	}

	/**
	 * Get the channel definition list for the SHeM type specified by the site's
	 * .shemtype file. If the .shemtype file does not exist, treat the SHeM as
	 * being the default type.
	 * 
	 * @return The channel definition list for the site's SHeM type.
	 * 
	 * @exception AscServiceAbortException
	 *                If an error is detected reading the SHeM type file.
	 */
	private MagnetDataChannelDef[] getChannelDefList()
			throws MagnetMonitorProcessorException, MagnetMonitorSiteException {
		IASCTextDataItem shemTypeItem = magMonSite.get(".shemtype");
		String typeLine = null;
		if (shemTypeItem.exists()) {
			IASCTextReader myReader = null;
			try {
				myReader = shemTypeItem.openForReading();
				typeLine = myReader.readLine();
				if (typeLine == null) {
					typeLine = "";
					ShemLogger.warn(".shemtype is empty", ShemLogger.SHEM_TEAM,
							systemId, minorFunction);
				}
				myReader.close();
			} catch (BasicException be) {
				myReader.silentlyClose();
				throw new MagnetMonitorProcessorException(be);
			}
		} else {
			String infoMessage = "No .shemtype file for " + systemId
					+ ", assuming default SHeM type";
			ASCLogger.info(infoMessage);
			typeLine = "";
		}
		MagnetDataChannelDefList value = loader
				.getMagnetDataChannelDefList(typeLine.toLowerCase());
		return value.getDefList();
	}

	/**
	 * Read through the specified text reader and return the last line. The
	 * reader is closed before returning.
	 * 
	 * @param myReader
	 *            The reader used to read the stream.
	 * 
	 * @return The last line of the stream. Returns null if the file is empty.
	 * 
	 * @exception AscServiceAbortException
	 *                If an I/O error occurs.
	 */
	private String readForLastLine(IASCTextReader myReader)
			throws MagnetMonitorProcessorException {
		String lastLineRead = null;
		try {
			for (String currentLine = myReader.readLine(); currentLine != null; currentLine = myReader
					.readLine()) {
				lastLineRead = currentLine;
			}
			myReader.close();
		} catch (BasicException be) {
			myReader.silentlyClose();
			throw new MagnetMonitorProcessorException(be);
		}
		return lastLineRead;
	}
}
